package com.hykj.xxltest.utils;

import com.alibaba.fastjson.JSON;
import com.hykj.xxltest.base.MsgException;
import com.hykj.xxltest.config.WeixinpayConfig;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

import javax.net.ssl.SSLContext;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.KeyStore;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1 文档地址
 * 微信支付回调校验工具类
 */
public class WechatpayNotifyVerifyUtil {

	private static Logger logger = Logger.getLogger(WechatpayNotifyVerifyUtil.class);

	private static String unifiedorder_url = "https://api.mch.weixin.qq.com/pay/unifiedorder";

	private static String refund_url="https://api.mch.weixin.qq.com/secapi/pay/refund";

	/**
	 * 生成预订单，获取支付请求参数
	 *
	 * @return
	 */
	public static String getPayReq(String orderCode, BigDecimal money, String ip) {
		BigDecimal price=money.multiply(new BigDecimal("100"));
		int fen=price.intValue();

		// 生成预支付交易单
		Map<String, String> params = prepay2(orderCode, fen, ip);
		// 生成发送的XML
		String reqXml = createUnifiedorderXml(params);
		// 生成预支付交易单请求
		String resp = HttpUtil.post(unifiedorder_url, reqXml);
		//
		return  getPrepayId(resp);

	}

	/**
	 * 网页支付
	 * 生成预支付交易单参数
	 * 
	 * @param orderId
	 * @param fee
	 * @return
	 */
	private static Map<String, String> prepay(String orderId, float fee, String openid, String ip) {
		Map<String, String> params = new HashMap<>();
		params.put("appid", WeixinpayConfig.appId);// 微信开放平台审核通过的应用APPID
		params.put("mch_id", WeixinpayConfig.mchId);// 微信支付分配的商户号
		// params.put("device_info", "");
		params.put("body", "LaoFoYe");// 商品或支付单简要描述
		params.put("trade_type", "JSAPI");// 支付类型
		params.put("nonce_str", RandomStringUtil.generateString(12));// 随机字符串，不长于32位
		params.put("notify_url", WeixinpayConfig.notifyUrl);// 接收微信支付异步通知回调地址，通知url必须为直接可访问的url，不能携带参数。
		params.put("out_trade_no", orderId);// 商户系统内部的订单号,32个字符内、可包含字母
		params.put("total_fee", String.valueOf((int) (fee * 100)));// 订单总金额，单位为分
		params.put("openid", openid);
		logger.error("prePay openid:——————————————————————" + openid);
		params.put("spbill_create_ip", ip);// 用户端实际ip
		logger.error("prePay ip:——————————————————————" + ip);
		String signStr = PayParamsUtil.createLinkString(params);// 拼接URL
		signStr += "&key=" + WeixinpayConfig.key;
		String sign = MD5.sign(signStr, "UTF-8").toUpperCase();// 签名
		params.put("sign", sign);
		logger.error("prePay sign:——————————————————————" + sign);
		return params;
	}

	/**
	 * 小程序支付
	 * 生成预支付交易单参数
	 * ip=request.getRemoteAddr()
	 *
	 * @return
	 */
	private static Map<String, String> prepay(String code, int money,String ip,String openid) {//单位分
		Map<String, String> params = new HashMap<>();
		params.put("appid", WeixinpayConfig.appId);// 微信开放平台审核通过的应用APPID
		params.put("mch_id", WeixinpayConfig.mchId);// 微信支付分配的商户号
		params.put("body", "浪全城");// 商品或支付单简要描述
		params.put("trade_type", "JSAPI");// 支付类型
		params.put("nonce_str", RandomStringUtil.generateString(12));// 随机字符串，不长于32位
		params.put("notify_url", WeixinpayConfig.notifyUrl);// 接收微信支付异步通知回调地址，通知url必须为直接可访问的url，不能携带参数。
		params.put("out_trade_no", code);// 商户系统内部的订单号,32个字符内、可包含字母
		params.put("total_fee", money+"");// 订单总金额，单位为分
		params.put("spbill_create_ip", ip);// 用户端实际ip
		params.put("openid",openid);
		logger.info(JSON.toJSONString(params));
		logger.error("prePay ip:——————————————————————" + ip);
		String signStr = PayParamsUtil.createLinkString(params);// 拼接URL
		signStr += "&key=" + WeixinpayConfig.key;
		String sign = MD5.sign(signStr, "UTF-8").toUpperCase();// 签名
		params.put("sign", sign);
		logger.error("prePay sign:——————————————————————" + sign);
		return params;
	}


	/**
	 * app支付
	 * 生成预支付交易单参数
	 * ip=request.getRemoteAddr()
	 *
	 * @return
	 */
	private static Map<String, String> prepay2(String code, int money,String ip) {//单位分

		Map<String, String> params = new HashMap<>();
		params.put("appid", WeixinpayConfig.appId);// 微信开放平台审核通过的应用APPID
		params.put("mch_id", WeixinpayConfig.mchId);// 微信支付分配的体验店号
		params.put("body", "hbr");// 商品或支付单简要描述
		params.put("trade_type", "APP");// 支付类型
		params.put("nonce_str", RandomStringUtil.generateString(12));// 随机字符串，不长于32位
		params.put("notify_url", WeixinpayConfig.notifyUrl);// 接收微信支付异步通知回调地址，通知url必须为直接可访问的url，不能携带参数。
		params.put("out_trade_no", code);// 体验店系统内部的订单号,32个字符内、可包含字母
		params.put("total_fee", money+"");// 订单总金额，单位为分
		params.put("spbill_create_ip", ip);// 用户端实际ip
		logger.error("prePay ip:——————————————————————" + ip);
		String signStr = PayParamsUtil.createLinkString(params);// 拼接URL
		signStr += "&key=" + WeixinpayConfig.key;

		String sign = MD5.sign(signStr, "UTF-8").toUpperCase();// 签名
		params.put("sign", sign);
		logger.error("prePay sign:——————————————————————" + sign);

		return params;
	}


	/**
	 * 生成支付请求参数
	 * 
	 * @param prepayId
	 * @return
	 */

	public static Map<String, String> pay(String prepayId) {
		Map<String, String> params = new HashMap<>();
		params.put("appid", WeixinpayConfig.appId);// 微信开放平台审核通过的应用APPID

		params.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));// 时间戳
		params.put("noncestr", RandomStringUtil.generateString(12));// 随机字符串，不长于32位
		params.put("package", "Sign=WXPay");// 暂填写固定值Sign=WXPay
		//params.put("package", "prepay_id=" + prepayId);
		//	params.put("signType", "MD5");
		params.put("partnerid", WeixinpayConfig.mchId);// 微信支付分配的体验店号
		params.put("prepayid", prepayId);// 微信返回的支付交易会话ID
		String signStr = PayParamsUtil.createLinkString(params);
		signStr += "&key=" + WeixinpayConfig.key;

		logger.error("pppp "+signStr);
		String sign = MD5.sign(signStr, "UTF-8").toUpperCase();// 签名


		params.put("sign", sign);

		logger.error("pay appId:——————————————————————" + params.get("appId"));
		logger.error("pay timestamp:——————————————————————" + params.get("timeStamp"));
		logger.error("pay noncestr:——————————————————————" + params.get("nonceStr"));
		logger.error("pay package:——————————————————————" + params.get("package"));
		logger.error("pay signType:——————————————————————" + "MD5");
		logger.error("pay sign:——————————————————————" + sign);
		return params;
	}

	/**
	 * 获取微信生成的预支付回话标识，用于后续接口调用中使用，该值有效期为2小时
	 * 
	 * @param resp
	 * @return
	 */
	private static String getPrepayId(String resp) {
		Map<String, String> respMap = parseXmlToMap(resp);
        logger.info(JSON.toJSONString(respMap));
		if (respMap == null) {
			throw new RuntimeException("返回参数为空");
		}
		String return_code = respMap.get("return_code");
		String return_msg = respMap.get("return_msg");
		if ("SUCCESS".equals(return_code)) {
			String result_code = respMap.get("result_code");
			if ("SUCCESS".equals(result_code)) {
				if (verifySign(respMap)) {
					String prepay_id = respMap.get("prepay_id");
					return prepay_id;
				} else {
					throw new RuntimeException("WeixinpayService.getPrepayId sign verify error!");
				}
			} else {
				String err_code = respMap.get("err_code");
				String err_code_des = respMap.get("err_code_des");
				throw new RuntimeException(String.format("错误编码:%s , 错误描述:%s", err_code, err_code_des));
			}

		} else {
			throw new RuntimeException(return_msg);
		}
	}

	/**
	 * 签名校验
	 * 
	 * @param params
	 * @return
	 */
	public static boolean verifySign(Map<String, String> params) {
		String sign = params.get("sign");
		Map<String, String> signMap = PayParamsUtil.paraFilter(params);
		String signStr = PayParamsUtil.createLinkString(signMap);
		signStr += "&key=" + WeixinpayConfig.key;
		return MD5.verify(signStr, sign, "UTF-8");
	}

	/**
	 * 将返回的XML转换成map
	 * 
	 * @param xml
	 * @return
	 */
	public static Map<String, String> parseXmlToMap(String xml) {
		try {
			Map<String, String> respMap = new HashMap<String, String>();
			Document respDoc = DocumentHelper.parseText(xml);
			Element root = respDoc.getRootElement();
			@SuppressWarnings("unchecked")
			List<Element> childList = root.elements();

			for (Element child : childList) {
				respMap.put(child.getName(), (String) child.getData());
			}
			return respMap;
		} catch (Exception ex) {
			logger.error("DocumentHelper.parseText error!", ex);
		}
		return null;
	}

	/**
	 * 将map转换成XML
	 * 
	 * @param params
	 * @return
	 */
	private static String createUnifiedorderXml(Map<String, String> params) {
		Element root = DocumentHelper.createElement("root");
		Document document = DocumentHelper.createDocument(root);
		for (String key : params.keySet()) {
			Element element = root.addElement(key);
			element.addText(params.get(key));
		}
		return document.asXML();
	}

	private static String getLocalIP() {
		String ip = "127.0.0.1";
		try {
			ip = InetAddress.getLocalHost().getHostAddress();
		} catch (UnknownHostException e) {
			logger.error("get local host error!", e);
		}
		return ip;
	}

	/**
	 * 微信退款
	 * ip=request.getRemoteAddr()
	 *
	 * @return
	 */
	private static Map<String, String> refundParms(String code, int money,String out_refund_no) {//单位分
		Map<String, String> params = new HashMap<>();
		params.put("appid", WeixinpayConfig.appId);// 微信开放平台审核通过的应用APPID
		params.put("mch_id", WeixinpayConfig.mchId);// 微信支付分配的商户号
		params.put("nonce_str", RandomStringUtil.generateString(12));// 随机字符串，不长于32位
		params.put("out_trade_no", code);// 商户系统内部的订单号,32个字符内、可包含字母
		params.put("total_fee", money+"");// 订单总金额，单位为分
		params.put("refund_fee", money+"");// 订单总金额，单位为分
		params.put("out_refund_no",out_refund_no);
		String signStr = PayParamsUtil.createLinkString(params);// 拼接URL
		signStr += "&key=" + WeixinpayConfig.key;
		String sign = MD5.sign(signStr, "UTF-8").toUpperCase();// 签名
		params.put("sign", sign);
		logger.error("prePay sign:——————————————————————" + sign);
		return params;
	}

	/**
	 * 生成预订单，获取支付请求参数
	 *
	 * @return
	 */
	public static boolean refund(String orderCode, BigDecimal money,String out_refund_no) throws Exception {
		BigDecimal price=money.multiply(new BigDecimal("100"));
		int fen=price.intValue();
		// 生成预支付交易单
		Map<String, String> params = refundParms(orderCode, fen,out_refund_no);

		// 生成发送的XML
		String reqXml = createUnifiedorderXml(params);
		// 生成预支付交易单请求
		KeyStore keyStore  = KeyStore.getInstance("PKCS12");
		FileInputStream instream = new FileInputStream(new File(WeixinpayConfig.cerPath));//放退款证书的路径
		try {
			keyStore.load(instream, WeixinpayConfig.mchId.toCharArray());
		} finally {
			instream.close();
		}

		SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, WeixinpayConfig.mchId.toCharArray()).build();
		SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
				sslcontext,
				new String[] { "TLSv1" },
				null,
				SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
		CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
		String sRes="";
		try {

			HttpPost httpPost = new HttpPost(refund_url);//退款接口

			System.out.println("executing request" + httpPost.getRequestLine());
			StringEntity reqEntity  = new StringEntity(reqXml);
			// 设置类型
			reqEntity.setContentType("application/x-www-form-urlencoded");
			httpPost.setEntity(reqEntity);
			CloseableHttpResponse response = httpclient.execute(httpPost);
			try {
				HttpEntity entity = response.getEntity();
				System.out.println("----------------------------------------");
				System.out.println(response.getStatusLine());
				if (entity != null) {
					System.out.println("Response content length: " + entity.getContentLength());
					BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent(),"UTF-8"));
					String text;
					while ((text = bufferedReader.readLine()) != null) {
						System.out.println(text);
						sRes+=text;
					}

				}
				EntityUtils.consume(entity);
			} finally {
				response.close();
			}
		} finally {
			httpclient.close();
		}

		 return getRefundRes(sRes);

	}


	/**
	 * 获取微信生成的预支付回话标识，用于后续接口调用中使用，该值有效期为2小时
	 *
	 * @param resp
	 * @return
	 */
	private static boolean getRefundRes(String resp) {
		Map<String, String> respMap = parseXmlToMap(resp);
		logger.info(JSON.toJSONString(respMap));
		if (respMap == null) {
			throw new RuntimeException("返回参数为空");
		}
		String return_code = respMap.get("return_code");
		String return_msg = respMap.get("return_msg");
		if ("SUCCESS".equals(return_code)) {
			String result_code = respMap.get("result_code");
			if ("SUCCESS".equals(result_code)) {
				if (verifySign(respMap)) {
					return true;
				} else {
					throw new RuntimeException("WeixinpayService.getPrepayId sign verify error!");
				}
			} else {
				String err_code = respMap.get("err_code");
				String err_code_des = respMap.get("err_code_des");
				throw new MsgException(String.format("错误编码:%s , 错误描述:%s", err_code, err_code_des));
			}

		} else {
			throw new RuntimeException(return_msg);
		}
	}

}
